package com.cn.nio;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

@Slf4j
public class NioServer {

  public static void main(String[] args) {

    NioServer newIO2 = new NioServer();
    newIO2.initServer();

  }

  public void initServer() {

    try {
      ServerSocketChannel serverChannel = ServerSocketChannel.open();
      serverChannel.bind(new InetSocketAddress(8090));
      //非阻塞
      serverChannel.configureBlocking(false);

      // selector select poll  。。。
      // 或者 epoll 中的 epoll_create 开辟一个空间
      Selector selector = Selector.open();

      // select,poll:jvm 里面开辟一个数组 文件描述符 放进去
      // epoll epoll_ctl (create的空间，add，，accept)；
      serverChannel.register(selector, SelectionKey.OP_ACCEPT);

      //服务器启动了
      System.out.println("服务启动了。。。");

      // select,poll 内核中 select(fd) poll(fd)
      // epoll epoll_wait

      while (true) {
        selector.select();
        System.out.println("获取监听");
        Set<SelectionKey> keyss = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = keyss.iterator();
        while (keyIterator.hasNext()) {
          SelectionKey key = keyIterator.next();
          // 不移除 会重新循环遍历，监听过的就丢弃即可
          keyIterator.remove();

          if (key.isAcceptable()) {
            // accept 建立连接
            ServerSocketChannel newserverChannel = (ServerSocketChannel) key.channel();
            SocketChannel channel = newserverChannel.accept();//接受连接请求
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ);

            System.out.println("新客服端建立了连接 " + channel.getRemoteAddress());
          } else if (key.isReadable()) {
            // 读取准备好的io
            SelectableChannel channel = key.channel();
            SocketChannel socketChannel = (SocketChannel) channel;

            log.info("key read -> {}",key);
            try {
              ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
              int read = socketChannel.read(byteBuffer);
              if (read == -1) {
                // 为什么 等于-1 要关闭 连接
                // 因为 客户端还是服务端 断开链接后,都会触发一次读事件,并 返回-1
                log.info("客服端主动断开了连接");
                socketChannel.close();
                key.cancel();
              }
              byteBuffer.flip();
              CharBuffer decode = StandardCharsets.UTF_8.decode(byteBuffer);
              log.info(decode.toString());
            } catch (IOException e) {
              e.printStackTrace();
              try {
                socketChannel.close();
              } catch (IOException ex) {
                ex.printStackTrace();
              }
            }
          } else if (key.isWritable()) {
            //为什么 监听不到 要关闭
            key.cancel();
          }
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }


}
